﻿//函数的实现

#include "Snake.h"

void SetPos(int x, int y)
{
	HANDLE houtput = NULL;
	//获取标准输出的句柄(⽤来标识不同设备的数值)
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos = { x, y };
	SetConsoleCursorPosition(houtput, pos);
}

void WelcomeGame()
{
	//欢迎界面
	SetPos(35, 18);
	wprintf(L"欢迎来到贪吃蛇小游戏");
	SetPos(36, 35);
	system("pause");
	system("cls");

	//使用说明界面
	SetPos(15, 18);
	wprintf(L"用↑、↓、←、→分别控制蛇的移动，F1为加速，F2为减速，加速将能得到更高的分数");
	SetPos(36, 35);
	system("pause");
	system("cls");
}

void CreateMap()
{
	//注意一个□横着占两个字节
	//上
	for (int i = 0; i < COL; i+=2)
	{
		//SetPos(i * 2, 0);
		//printf("%d ", i);
		//SetPos(i * 2, 1);
		wprintf(L"□");
	}

	//下
	SetPos(0, ROW);
	for (int i = 0; i < COL; i+=2)
		wprintf(L"□");

	//左
	for (int i = 1; i < ROW; i++)
	{
		SetPos(0, i);
		wprintf(L"□");
	}

	//右
	for (int i = 1; i < ROW; i++)
	{
		SetPos((COL - 2), i);
		wprintf(L"□");
	}
 }

void InitSnake(pSnake snake)
{
	//初始化头的位置为(InitX, InitY),有5个节点
	//给蛇分配空间
	pSnakeNode prenode = NULL;
	for (int i = 0; i < 5; i++)
	{
		pSnakeNode pnode = (pSnakeNode)malloc(sizeof(SnakeNode));
		if (pnode == NULL)
		{
			perror("InitSnake::malloc error");
			exit(0);
		}

		//if (i == 0)
		//	snake->head = pnode;

		////我写的尾插
		//if (prenode != NULL)
		//	prenode->next = pnode;
		//else
		//	snake->head = pnode;
		//prenode = pnode;
		//pnode->x = InitX - i * 2;
		//pnode->y = InitY;
		//pnode->next = NULL;

		//头插

		pnode->x = InitX + i * 2;//注意是头插：要是+号啊！！！！！！！！！！！
		pnode->y = InitY;
		pnode->next = NULL;
		if (snake->head == NULL)
			snake->head = pnode;
		else
		{
			pnode->next = snake->head;
			snake->head = pnode;
		}
	}

	//打印
	pSnakeNode cur = NULL;
	cur = snake->head;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"●");
		cur = cur->next;
	}

	//设置蛇的初始方向
	snake->dir = RIGHT;
	//初始化蛇的方向
	snake->score = 0;
	//
	snake->foodscore = FoodScore;
	snake->SleepTime = Normal;
	snake->status = NORMAL;

	//getchar();
}

void SetFood(pSnake snake)
{
	int x, y;
again:
	do
	{
		x = rand() % 54 + 2;//2~55<----------0~53
		y = rand() % 27 + 1;//1~27<----------0~26
	} while (x % 2 != 0);//食物要在双数位置才合理,才吃的到

	//注意不能和蛇身重合
	pSnakeNode cur = snake->head;
	while (cur)
	{
		if (x == cur->x && y == cur->y)
		{
			goto again;
		}
		cur = cur->next;
	}

	//创建食物，是一个结构体，要申请空间
	pSnakeNode food = (pSnakeNode)malloc(sizeof(SnakeNode));

	if (food == NULL)
	{
		perror("SetFood::malloc error");
		exit(0);
	}
	else
	{
		food->x = x;
		food->y = y;
		
		//SetPos(x, y - 1);
		//printf("(%d, %d)", x, y);

		SetPos(x, y);
		wprintf(L"★");
		snake->food = food;
	}
}

void PrintHelpInfo()
{
	//SetPos(COL * 2 + 2, ROW / 2 - 4);//得分

	SetPos(COL + 2, ROW / 2 + 2);
	wprintf(L"不能穿墙，不能咬到自己");
	SetPos(COL + 2, ROW / 2 + 3);
	wprintf(L"用↑、↓、←、→分别控制蛇的移动");
	SetPos(COL + 2, ROW / 2 + 4);
	wprintf(L"F1为加速，F2为减速");
	SetPos(COL + 2, ROW / 2 + 5);
	wprintf(L"ESC：退出游戏，space暂停游戏");//注意，空格就是暂停了
}

void Pause()
{
	while (1)
	{
		Sleep(200);
		if (KEY_PRESS(VK_SPACE))
			break;
	}
}


int NextIsFood(pSnake snake, pSnakeNode AddNode)
{
	return (snake->food->x == AddNode->x && snake->food->y == AddNode->y);
}

void EatFood(pSnake snake, pSnakeNode AddNode)
{
	//法一：
	//释放食物空间
	//变长

	//重新生成节点
	//SetFood(snake);

	//法二：
	//直接把食物的节点挂上去
	AddNode->next = snake->head;
	snake->head = AddNode;

	//打印蛇
	pSnakeNode cur = snake->head;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"●");
		cur = cur->next;
	}

	//加分数
	snake->score += FoodScore;

	//记得释放食物节点空间，因为不仅食物是占有这个节点，AddNode也占有
	free(snake->food);
	snake->food = NULL;

	SetFood(snake);
}

void NoFood(pSnake snake, pSnakeNode AddNode)
{
	AddNode->next = snake->head;
	snake->head = AddNode;

	//释放最后一个节点，并将其变成两个空格
	pSnakeNode cur = snake->head;
	//释放最后一个节点，可以最后GameEnd再释放，会加快效率:要记录一下蛇有几个节点了，方便打印
	//打印蛇
	while (cur->next->next)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"●");
		cur = cur->next;
	}
	SetPos(cur->next->x, cur->next->y);
	wprintf(L"  ");

	free(cur->next);
	cur->next = NULL;
}

//注意一开始的方向
//改变方向：添加第一个节点，删除最后一个节点，而不用更改每个节点
void SnakeMove(pSnake snake)
{
	pSnakeNode AddNode = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (AddNode == NULL)
	{
		perror("SnakeMove::malloc error");
		exit(0);
	}

	pSnakeNode cur = snake->head;
	if (snake->dir == RIGHT)
	{
		AddNode->x = snake->head->x + 2;
		AddNode->y = snake->head->y;
	}
	else if (snake->dir == LEFT)
	{
		AddNode->x = snake->head->x - 2;
		AddNode->y = snake->head->y;
	}
	else if (snake->dir == UP)
	{
		AddNode->x = snake->head->x;
		AddNode->y = snake->head->y - 1;
	}
	else if (snake->dir == DOWN)
	{
		AddNode->x = snake->head->x;
		AddNode->y = snake->head->y + 1;
	}

	//SetPos(AddNode->x, AddNode->y - 1);
	//printf("(%d, %d)", AddNode->x, AddNode->y);

	//判断下一个节点是不是食物
	if (NextIsFood(snake, AddNode))
	{
		EatFood(snake, AddNode);
	}
	else
	{
		NoFood(snake, AddNode);
	}

	CheakStatus(snake, AddNode);
}

void CheakStatus(pSnake snake, pSnakeNode AddNode)
{
	if (snake->status == NORMAL)
	{
		//检查撞墙
		if (AddNode->x == 0 || AddNode->x == COL - 2||
			AddNode->y == 0 || AddNode->y == ROW)
		{
			snake->status = KILL_BY_WALL;
			return;
		}

		//检查撞自己
		pSnakeNode cur = snake->head->next;//注意是next
		while (cur)
		{
			if (cur->x == AddNode->x && cur->y == AddNode->y)
			{
				snake->status = KILL_BY_SELF;
				return;
			}
			cur = cur->next;
		}
	}
}

void GameStart(pSnake snake)
{
	system("mode con cols=100 lines=40");
	system("title 贪吃蛇");

	//隐藏光标
	//获得句柄
	HANDLE houtput = NULL;
	//获取标准输出的句柄(⽤来标识不同设备的数值)
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);

	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(houtput, &CursorInfo);//获取控制台光标信息
	CursorInfo.bVisible = false;//设置光标不可见
	SetConsoleCursorInfo(houtput, &CursorInfo);//设置控制台光标状态

	//打印欢迎界面
	WelcomeGame();
	//设置地图
	CreateMap();
	//初始化蛇
	InitSnake(snake);
	//设置食物位置
	SetFood(snake);

	//getchar();
}

void GameRun(pSnake snake)
{
	//设置提示界面
	PrintHelpInfo();

	//pSnakeNode cur = snake->head;
	////释放最后一个节点，可以最后GameEnd再释放，会加快效率:要记录一下蛇有几个节点了，方便打印
	////打印蛇
	//while (cur)
	//{
	//	printf("(%d, %d)", cur->x, cur->y);
	//	cur = cur->next;
	//}

	do
	{
		SetPos(COL + 2, ROW / 2 - 4);//得分
		wprintf(L"当前得分：%d 每个食物得分：%d", snake->score, snake->foodscore);
		
		if (KEY_PRESS(VK_UP) && snake->dir != DOWN)
		{
			snake->dir = UP;
		}
		else if (KEY_PRESS(VK_DOWN) && snake->dir != UP)
		{
			snake->dir = DOWN;
		}
		else if (KEY_PRESS(VK_RIGHT) && snake->dir != LEFT)
		{
			snake->dir = RIGHT;
		}
		else if (KEY_PRESS(VK_LEFT) && snake->dir != RIGHT)
		{
			snake->dir = LEFT;
		}
		else if (KEY_PRESS(VK_SPACE))
		{
			//暂停：死循环的睡眠，直到按了空格
			Pause();
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			//正常退出：游戏状态设置为ESC
			snake->status = ESC;
		}
		else if (KEY_PRESS(VK_F1))
		{
			//加速：按一下就加速
			if (snake->SleepTime > Normal - 4 * TimeChange)
			{
				snake->SleepTime -= TimeChange;
				snake->foodscore += FoodScoreChange;
			}
		}
		else if (KEY_PRESS(VK_F2))
		{
			//减速：按一下就减速
			if (snake->SleepTime < Normal - 4 * TimeChange)
			{
				snake->SleepTime += TimeChange;
				snake->foodscore -= FoodScoreChange;
			}
		}
	
		//贪吃蛇走一步
		SnakeMove(snake);
		Sleep(snake->SleepTime);
		//Sleep(1000);
	} while (snake->status == NORMAL);

	//getchar();
}

void GameEnd(pSnake snake)
{
	SetPos(COL / 2, ROW / 2);
	if (snake->status == KILL_BY_WALL)
		wprintf(L"很遗憾，你撞墙嘎了");
	if (snake->status == KILL_BY_SELF)
		wprintf(L"很遗憾，你撞自己嘎了");
	if (snake->status == ESC)
		wprintf(L"正常退出");

	pSnakeNode cur = snake->head, cur1;
	cur1 = cur;

	while (cur)
	{
		cur1 = cur;
		cur = cur->next;
		free(cur1);
		cur1 = NULL;
	}

	//getchar();
	//system("pause");
}